package logic

import (
	"context"
	"strconv"
	"strings"

	"godco/internal/svc"
	"godco/internal/types"
	"godco/internal/utils"

	"github.com/zeromicro/go-zero/core/logx"
)

type MergeHookLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewMergeHookLogic(ctx context.Context, svcCtx *svc.ServiceContext) *MergeHookLogic {
	return &MergeHookLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *MergeHookLogic) MergeHook(req *types.MergeHookRequest) (resp *types.MergeHookResp, err error) {
	// todo: add your logic here and delete this line
	//l.Debugf("request: %v\n", *req)
	l.Debug(">> MergeHook ENTER:\n")
	l.Debugf("action:%s", req.Action)

	keyWord := "checkDco"
	if req.Action == "created" {

		if req.Comment != nil {
			if req.Comment.Body != keyWord {
				res := types.MergeHookResp{Code: 0, Message: "return Action created success"}
				l.Debugf("action:%s return,body:%s,keyword:%s", req.Action, req.Comment.Body, keyWord)
				return &res, nil
			}
		}
	}
	l.Debugf("pull_request_url:%s", req.PullRequest.Links.HTML.Href)
	l.Debugf("处理仓库:%s", req.Repository.GitURL)
	l.Debugf("分支名:%s", req.PullRequest.Head.Ref)
	l.Debugf("组织名:%s", req.Organiz.Login)
	l.Debugf("InstallID:%d", req.Install.ID)
	l.Debugf("验证提交ID:%s", req.PullRequest.Head.SHA)
	l.Debugf("提交者:%s", req.PullRequest.Head.User.Name)
	l.Debugf("提交者邮箱:%s", req.PullRequest.Head.User.Email)

	result := "DCO验证结果:"

	isSign := false
	isDcoOK := false
	// 检查DCO签名是否通过
	l.Debug("检查DCO验证是否通过...")
	auth, err := l.svcCtx.AuthModel.FindOneByInstallID(l.ctx, strconv.FormatInt(req.Install.ID, 10))
	if err == nil {
		//l.Debugf("commit message:%s", req.Commits[0].Message)
		l.Debug("开始验证DCO签名...")
		tkResp, err := utils.RefreshToken(TokenUrl, l.svcCtx.Config.Client.ClientID, l.svcCtx.Config.Client.ClientSecret, auth.TokenF)
		if err == nil {
			auth.Token = tkResp.AccessToken
			auth.TokenF = tkResp.RefreshToken
			l.Debugf("token:%s", auth.Token)
			_, err = l.svcCtx.AuthModel.Update(l.ctx, auth)
			if err != nil {
				l.Error("Error Update token!")
			}
		}
		fullname := req.Repository.FullName
		parts := strings.Split(fullname, "/")
		owner := parts[0]
		repo := parts[1]

		info, err := utils.GetCommitsInfo(owner, repo, req.PullRequest.Head.SHA, auth.Token)
		if err != nil {
			l.Debug("DCO签名验证失败!")
			result += "验证失败"
			return nil, err
		}
		l.Debugf("message:%s", info.Message)
		// 检查是否签署了dco协议
		l.Debug("检查邮箱是否已签署DCO协议...")
		_, err = l.svcCtx.UserModel.FindOneByEmail(l.ctx, info.Email)
		if err != nil {
			l.Debugf("UserModel.FindOneByEmail error:", err.Error())
			l.Info("未签署DCO声明协议")
			to := info.Email
			title := "DCO声明协议未签署"
			body := "未签署DCO声明协议,\n请访问网址:https://dco016.atomgit.net/static/dco/sign.html 进行签署。"

			result += body
			go l.sendEmail(to, title, body)
		} else {
			isSign = true
		}
		// 验证DCO签名
		if utils.CheckSignedOffBy(info.Message) {
			l.Debug("ok, it contains Signed-off-by")
			name, email, err := utils.GetSignedOffBy(info.Message)
			if err != nil {
				l.Error("Error getSignedOffBy")
				l.Debug("未通过DCO验证")
				result += "验证失败," + err.Error()
			} else {
				l.Debugf("Signed-off-by name:%s", name)
				l.Debugf("Signed-off-by email:%s", email)

				if email == info.Email {
					l.Debug("ok, dco check ok")
					if isSign {
						isDcoOK = true
						l.Debug("通过DCO验证")
						result += "DCO验证通过"
					}
				} else {
					l.Debug("error, dco check fail")
					l.Debug("未通过DCO验证")
					result += "未通过DCO验证,邮箱不一致!" + "签名者邮箱:" + email + ",提交者邮箱:" + info.Email
				}
			}
		} else {
			l.Error("dco check error, not contains Signed-off-by")
			l.Debug("未通过DCO验证")
			result += "验证失败,未通过DCO验证"
		}
		// l.Debug("DCO验证结果入库")
		// err = l.svcCtx.ResultModel.InsertOrUpdate(l.ctx, &result)
		// if err != nil {
		// 	l.Debug("DCO验证结果入库失败:" + err.Error())
		// } else {
		// 	l.Debug("DCO验证结果入库成功")
		// }

	} else {
		l.Debug("DCO验证失败!")
		result += "验证失败!" + err.Error()
	}

	if req.Comment == nil || req.Comment.Body == keyWord {
		if auth != nil {
			fullname := req.Repository.FullName
			parts := strings.Split(fullname, "/")
			if len(parts) > 1 {
				owner := parts[0]
				repo := parts[1]
				go l.creatComment(owner, repo, strconv.Itoa(req.PullRequest.Number), result, req.PullRequest.Head.SHA, auth.Token)
				lables := []string{}
				if isDcoOK {
					lables = append(lables, "DCO Signed")
				} else {
					lables = append(lables, "DCO Failed")
				}
				go l.creatPRLabels(owner, repo, strconv.Itoa(req.PullRequest.Number), auth.Token, lables)
			}
		}
	}

	res := types.MergeHookResp{Code: 0, Message: "success"}

	return &res, nil
}

func (l *MergeHookLogic) sendEmail(to string, title string, body string) (err error) {

	l.Debug(">> 开始发送邮件...")
	err = utils.SendEmail(l.svcCtx.Config.EConf.From, l.svcCtx.Config.EConf.Pwd, l.svcCtx.Config.EConf.Server, to, title, body, l.svcCtx.Config.EConf.ContentType)
	if err != nil {
		l.Debugf("<< 发送邮件失败:%s", err.Error())
	} else {
		l.Debug("<< 发送邮件成功")
	}
	return err
}

func (l *MergeHookLogic) creatComment(owner, repo, pull_number, body, commit_id, token string) (err error) {

	l.Debug(">> 开始创建DCO评论...")
	err = utils.CreatePRComment(owner, repo, pull_number, body, commit_id, token)
	if err != nil {
		l.Debugf("<< 创建DCO评论失败:%s", err.Error())
	} else {
		l.Debug("<< 创建DCO评论成功")
	}
	return err
}

// 判断一个数组是否包含另一个数组的所有内容
func isContainsLabel(lbs []string, lables []string) bool {
	lbsMap := make(map[string]struct{})

	// 将 lbs 数组中的元素存入 map
	for _, lb := range lbs {
		lbsMap[lb] = struct{}{}
	}

	// 检查 lables 中的每个元素是否在 map 中
	for _, label := range lables {
		if _, exists := lbsMap[label]; !exists {
			return false
		}
	}
	return true
}

func (l *MergeHookLogic) creatPRLabels(owner, repo, pull_number, token string, lables []string) (err error) {

	l.Debug(">> 开始创建PR标签...")
	// 先查寻是否存在标签，不存在则创建
	l.Debug(">> 获取仓库标签")
	lbs, err := utils.GetDcoLabels(owner, repo, token)
	if err != nil {
		l.Debugf("<< 获取仓库标签失败:%s", err.Error())
		return err
	}
	l.Debug(">> 检查DCO标签是否存在")
	isContain := isContainsLabel(lbs, lables)

	if !isContain {
		l.Debug(">> 创建DCO标签")
		err1 := utils.CreateDcoLabels(owner, repo, "DCO Signed", "#00FF00", token)
		err2 := utils.CreateDcoLabels(owner, repo, "DCO Failed", "#FF0000", token)
		if err1 != nil && err2 != nil {
			l.Debugf("<< 创建DCO标签失败:%s", err1.Error())
			return err
		}
	}

	err = utils.CreatePRLabels(owner, repo, pull_number, token, lables)
	if err != nil {
		l.Debugf("<< 创建PR标签失败:%s", err.Error())
	} else {
		l.Debug("<< 创建PR标签成功")
	}
	return err
}
